/*-------------------------------------------------------------------------
 *
 * c.h
 *	  基本 C 定义。这个头文件被每个 PostgreSQL 的 .c 文件包含
 *	  （通过 postgres.h 或 postgres_fe.h，根据需要）。
 *
 *	  请注意，此处的定义并不打算暴露给前端接口库的客户端——
 *	  因此我们不太担心用太多东西污染命名空间……
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/c.h
 *
 *-------------------------------------------------------------------------
 */
/*
 *----------------------------------------------------------------
 *	 目录
 *
 *		在向此文件添加内容时，请尽量将内容放入相关部分，
 *		或根据需要添加新部分。
 *
 *	  部分		描述
 *	  -------	------------------------------------------------
 *		0)		pg_config.h 和标准系统头文件
 *		1)		编译器特性
 *		2)		bool、true、false
 *		3)		标准系统类型
 *		4)		系统类型的 IsValid 宏
 *		5)		offsetof、lengthof、alignment
 *		6)		断言
 *		7)		广泛使用的宏
 *		8)		随机内容
 *		9)		系统特定的 hacks
 *
 * 注意：由于此文件被前端和后端模块同时包含，
 * 通常在这里放置 "extern" 声明是错误的，除非它被
 * ifdef 包裹，以便仅在其中一种情况下看到。
 * typedef 和宏可能会放在这里。
 *
 *----------------------------------------------------------------
 */
#ifndef C_H
#define C_H

#include "postgres_ext.h"

/* 在包含 pg_config.h 之前必须取消定义 pg_config_ext.h 符号 */
#undef PG_INT64_TYPE

#include "pg_config.h"
#include "pg_config_manual.h"	/* 必须在 pg_config.h 之后 */
#include "pg_config_os.h"		/* 必须在任何系统头文件之前 */

/* 应该在 Postgres 中随处可用的系统头文件 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <stdarg.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <stdint.h>
#include <sys/types.h>
#include <errno.h>
#if defined(WIN32) || defined(__CYGWIN__)
#include <fcntl.h>				/* 确保 O_BINARY 可用 */
#endif
#include <locale.h>
#ifdef ENABLE_NLS
#include <libintl.h>
#endif


/* ----------------------------------------------------------------
 *				第 1 部分：编译器特性
 *
 * 类型前缀（const、signed、volatile、inline）在 pg_config.h 中处理。
 * ----------------------------------------------------------------
 */

/*
 * 如果定义了 PG_FORCE_DISABLE_INLINE，则禁用 "inline"。
 * 这用于解决编译器错误，并且在调查目的上也可能有用。
 */
#ifdef PG_FORCE_DISABLE_INLINE
#undef inline
#define inline
#endif

/*
 * 属性宏
 *
 * GCC: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
 * GCC: https://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html
 * Clang: https://clang.llvm.org/docs/AttributeReference.html
 * Sunpro: https://docs.oracle.com/cd/E18659_01/html/821-1384/gjzke.html
 * XLC: https://www.ibm.com/support/knowledgecenter/SSGH2K_13.1.2/com.ibm.xlc131.aix.doc/language_ref/function_attributes.html
 * XLC: https://www.ibm.com/support/knowledgecenter/SSGH2K_13.1.2/com.ibm.xlc131.aix.doc/language_ref/type_attrib.html
 */

/*
 * 对于不支持 __has_attribute 的编译器，我们仅将
 * __has_attribute(x) 定义为 0，以便我们可以更轻松地为各种
 * __attribute__s 定义宏。
 */
#ifndef __has_attribute
#define __has_attribute(attribute) 0
#endif

/* 仅 GCC 支持未使用属性 */
#ifdef __GNUC__
#define pg_attribute_unused() __attribute__((unused))
#else
#define pg_attribute_unused()
#endif

/*
 * pg_nodiscard 意味着如果忽略了函数调用的结果，编译器应该发出警告。
 * "nodiscard" 这个名称是为了与（可能的未来）C 和 C++ 标准保持一致。
 * 为了最大兼容性，作为函数声明说明符使用它，以便它位于返回类型之前。
 */
#ifdef __GNUC__
#define pg_nodiscard __attribute__((warn_unused_result))
#else
#define pg_nodiscard
#endif

/*
 * 在应该允许进行不对齐访问的函数之前放置此宏。
 * 在非 x86 特定代码上使用它时要三思而后行！
 * 可以使用 "-fsanitize=alignment -fsanitize-trap=alignment"
 * 在 clang 上进行测试，或在 gcc 上使用 "-fsanitize=alignment -fno-sanitize-recover=alignment"。
 */
#if __clang_major__ >= 7 || __GNUC__ >= 8
#define pg_attribute_no_sanitize_alignment() __attribute__((no_sanitize("alignment")))
#else
#define pg_attribute_no_sanitize_alignment()
#endif

/*
 * 在仅在启用断言构建中使用的变量的定义中附加 PG_USED_FOR_ASSERTS_ONLY，
 * 以避免在禁用断言构建中产生关于未使用变量的编译器警告。
 */
#ifdef USE_ASSERT_CHECKING
#define PG_USED_FOR_ASSERTS_ONLY
#else
#define PG_USED_FOR_ASSERTS_ONLY pg_attribute_unused()
#endif

/* GCC 和 XLC 支持格式属性 */
#if defined(__GNUC__) || defined(__IBMC__)
#define pg_attribute_format_arg(a) __attribute__((format_arg(a)))
#define pg_attribute_printf(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE, f, a)))
#else
#define pg_attribute_format_arg(a)
#define pg_attribute_printf(f,a)
#endif

/* GCC、Sunpro 和 XLC 支持对齐、打包和 noreturn */
#if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
#define pg_attribute_aligned(a) __attribute__((aligned(a)))
#define pg_attribute_noreturn() __attribute__((noreturn))
#define pg_attribute_packed() __attribute__((packed))
#define HAVE_PG_ATTRIBUTE_NORETURN 1
#else
/*
 * 注意：对齐和打包没有给出默认定义，因为它们
 * 会影响代码功能；如果希望使用它们，编译器
 * 必须实现它们。
 */
#define pg_attribute_noreturn()
#endif

/*
 * 在我们希望强制内联的函数中使用 "pg_attribute_always_inline" 来替代 "inline"，即使编译器的启发式会选择不内联。但如果可能，不要在未优化的调试构建中强制内联。
 */
#if (defined(__GNUC__) && __GNUC__ > 3 && defined(__OPTIMIZE__)) || defined(__SUNPRO_C) || defined(__IBMC__)
/* GCC > 3、Sunpro 和 XLC 通过 __attribute__ 支持 always_inline */
#define pg_attribute_always_inline __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
/* MSVC有一个特殊的关键字用于此 */
#define pg_attribute_always_inline __forceinline
#else
/* 否则，我们所能做的就是说“内联” */
#define pg_attribute_always_inline inline
#endif

/*
 * 强制函数不进行内联在性能关键函数的慢路径上是有用的，
 * 或者应该在性能分析中可见，以允许正确的成本归属。请注意，与
 * 上面的pg_attribute_XXX宏不同，这应该放在函数的返回类型和名称之前。
 */
/* GCC、Sunpro和XLC通过__attribute__支持noinline */
#if (defined(__GNUC__) && __GNUC__ > 2) || defined(__SUNPRO_C) || defined(__IBMC__)
#define pg_noinline __attribute__((noinline))
/* msvc通过declspec */
#elif defined(_MSC_VER)
#define pg_noinline __declspec(noinline)
#else
#define pg_noinline
#endif

/*
 * 暂时将pg_attribute_cold和pg_attribute_hot定义为空宏
 * 在minGW 8.1上。似乎存在一个编译器错误导致编译失败。
 * 此时，我们仍然有至少一个构建农场动物在运行该编译器，因此这应该
 * 使其再次变为绿色。这个编译器可能不够流行，不值得永远保留这些代码，
 * 所以在最后一个构建农场动物升级后，我们就删除它。
 */
#if defined(__MINGW64__) && __GNUC__ == 8 && __GNUC_MINOR__ == 1

#define pg_attribute_cold
#define pg_attribute_hot

#else
/*
 * 将某些函数标记为“热”或“冷”可以帮助编译器以更高效的方式
 * 组织汇编代码。
 */
#if __has_attribute (cold)
#define pg_attribute_cold __attribute__((cold))
#else
#define pg_attribute_cold
#endif

#if __has_attribute (hot)
#define pg_attribute_hot __attribute__((hot))
#else
#define pg_attribute_hot
#endif

#endif							/* defined(__MINGW64__) && __GNUC__ == 8 &&
								 * __GNUC_MINOR__ == 1 */
/*
 * 以可移植的方式标记一个点为不可达。这应该是编译器可以理解的东西，
 * 以帮助代码生成。在启用断言的构建中，我们更喜欢使用abort()以
 * 进行调试原因。
 */
#if defined(HAVE__BUILTIN_UNREACHABLE) && !defined(USE_ASSERT_CHECKING)
#define pg_unreachable() __builtin_unreachable()
#elif defined(_MSC_VER) && !defined(USE_ASSERT_CHECKING)
#define pg_unreachable() __assume(0)
#else
#define pg_unreachable() abort()
#endif

/*
 * 向编译器提示分支的可能性。likely()和unlikely()都返回包含表达式的
 * 布尔值。
 *
 * 这些应该仅在非常热的代码路径中谨慎使用。很容易错误估计可能性。
 */
#if __GNUC__ >= 3
#define likely(x)	__builtin_expect((x) != 0, 1)
#define unlikely(x) __builtin_expect((x) != 0, 0)
#else
#define likely(x)	((x) != 0)
#define unlikely(x) ((x) != 0)
#endif

/*
 * CppAsString
 *		使用C预处理器将参数转换为字符串。
 * CppAsString2
 *		在进行一轮宏展开后，将参数转换为字符串。
 * CppConcat
 *		使用C预处理器将两个参数连接在一起。
 *
 * 注意：这里曾经支持不支持#和##的预ANSI C编译器。如今，这些宏
 * 仅为清晰度和/或与现有PostgreSQL代码向后兼容而存在。
 */
#define CppAsString(identifier) #identifier
#define CppAsString2(x)			CppAsString(x)
#define CppConcat(x, y)			x##y

/*
 * VA_ARGS_NARGS
 *		返回传递给它的宏参数的数量。
 *
 * 空参数仍被视为一个参数，因此实际上这是“参数列表中逗号数量加一”。
 *
 * 这适用于最多63个参数。在内部，VA_ARGS_NARGS_()被传递64+N个参数，
 * C99标准只要求宏允许最多127个参数，因此我们不能可移植地更高。
 * 实现相当简单：VA_ARGS_NARGS_()返回其第64个参数，我们设置
 * 调用使其成为常量列表中适当的参数。这个想法源于Laurent Deniau。
 */
#define VA_ARGS_NARGS(...) \
	VA_ARGS_NARGS_(__VA_ARGS__, \
				   63,62,61,60,                   \
				   59,58,57,56,55,54,53,52,51,50, \
				   49,48,47,46,45,44,43,42,41,40, \
				   39,38,37,36,35,34,33,32,31,30, \
				   29,28,27,26,25,24,23,22,21,20, \
				   19,18,17,16,15,14,13,12,11,10, \
				   9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define VA_ARGS_NARGS_( \
	_01,_02,_03,_04,_05,_06,_07,_08,_09,_10, \
	_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
	_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
	_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
	_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
	_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
	_61,_62,_63,  N, ...) \
	(N)

/*
 * dummyret用于在使用?:进行赋值的宏中设置返回值。
 * gcc希望这些为void，其他编译器喜欢char
 */
#ifdef __GNUC__					/* GNU cc */
#define dummyret	void
#else
#define dummyret	char
#endif

/*
 * 通用函数指针。在极少数情况下，可以将函数指针强制转换为看似不兼容的
 * 函数指针类型，同时避免gcc的-Wcast-function-type警告。
 */
typedef void (*pg_funcptr_t) (void);

/*
 * 我们要求C99，因此编译器应该理解灵活数组成员。
 * 但是，出于文档目的，我们仍然认为以“field[FLEXIBLE_ARRAY_MEMBER]”
 * 而不仅仅是“field[]”来编写是项目风格。当计算此类对象的大小时，
 * 使用“offsetof(struct s, f)”以实现可移植性。不要使用
 * “offsetof(struct s, f[0])”，因为这在MSVC和C++编译器中不起作用。
 */
#define FLEXIBLE_ARRAY_MEMBER	/* 空 */

/* 我们有什么__func__符号，如果有的话？ */
#ifdef HAVE_FUNCNAME__FUNC
#define PG_FUNCNAME_MACRO	__func__
#else
#ifdef HAVE_FUNCNAME__FUNCTION
#define PG_FUNCNAME_MACRO	__FUNCTION__
#else
#define PG_FUNCNAME_MACRO	NULL
#endif
#endif

/*
 * 编译器支持#pragma GCC system_header吗？我们可选地使用它
 * 以避免无法修复的警告（例如在perl头文件中）。
 * 请参见 https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html
 *
 * 我们不想显示编译器警告的头文件可以
 * 有条件地使用#pragma GCC system_header以避免警告。显然
 * 这只应在我们无法控制的外部头文件中使用。
 *
 * 这条指令的支持在这里进行测试，而不是在配置期间，因为gcc
 * 也会警告在.c文件中使用指令。要让autoconf使用.h作为文件后缀
 * 真的很困难。看起来gcc自2000年以来就实现了这条指令，因此此测试
 * 应该足够。
 *
 *
 * 另外，我们可以使用-isystem为问题头文件添加包含路径，
 * 但那是一个更大的锤子且更难搜索。
 *
 * 更细粒度的替代方案是使用#pragma GCC diagnostic
 * push/ignored/pop，但gcc会警告关于未知警告被忽略，
 * 因此每个待暂时忽略的编译器警告都需要自己的pg_config.h符号
 * 和#ifdef。
 */
#ifdef __GNUC__
#define HAVE_PRAGMA_GCC_SYSTEM_HEADER	1
#endif



/* ----------------------------------------------------------------
 *				Section 2:	bool, true, false
 * ----------------------------------------------------------------
 */

/*
 * bool
 *		布尔值，可以是 true 或 false。
 *
 * 如果 bool 的大小在包含后为 1，我们使用 stdbool.h。这对于
 * 更好的编译器和调试器输出以及与第三方库的兼容性是有用的。
 * 但 PostgreSQL 当前无法处理其他大小的 bool；
 * 代码周围有静态断言以防止这种情况发生。
 *
 * 对于 C++ 编译器，我们假设编译器有一个兼容的内置
 * bool 定义。
 *
 * 另请参见位于 src/interfaces/ecpg/include/ecpglib.h 中的此代码版本。
 */

#ifndef __cplusplus

#ifdef PG_USE_STDBOOL
#include <stdbool.h>
#else

#ifndef bool
typedef unsigned char bool;
#endif

#ifndef true
#define true	((bool) 1)
#endif

#ifndef false
#define false	((bool) 0)
#endif

#endif							/* 不是PG_USE_STDBOOL */
#endif							/* 不是C++ */


/* ----------------------------------------------------------------
 *				Section 3:	标准系统类型
 * ----------------------------------------------------------------
 */

/*
 * Pointer
 *		保存任何内存常驻对象地址的变量。
 *
 *		XXX 指针运算使用此项，因此在 "true" ANSI 编译器下不能是 void *
 */
typedef char *Pointer;

/*
 * intN
 *		有符号整数，大小恰好为 N 位，
 *		用于数值计算以及
 *		前端/后端协议。
 */
#ifndef HAVE_INT8
typedef signed char int8;		/* == 8 bits */
typedef signed short int16;		/* == 16 bits */
typedef signed int int32;		/* == 32 bits */
#endif							/* not HAVE_INT8 */

/*
 * uintN
 *		无符号整数，大小恰好为 N 位，
 *		用于数值计算以及
 *		前端/后端协议。
 */
#ifndef HAVE_UINT8
typedef unsigned char uint8;	/* == 8 bits */
typedef unsigned short uint16;	/* == 16 bits */
typedef unsigned int uint32;	/* == 32 bits */
#endif							/* not HAVE_UINT8 */

/*
 * bitsN
 *		位运算单位，大小至少为 N 位。
 */
typedef uint8 bits8;			/* >= 8 位 */
typedef uint16 bits16;			/* >= 16 位 */
typedef uint32 bits32;			/* >= 32 位 */

/*
 * 64 位整数
 */
#ifdef HAVE_LONG_INT_64
/* 普通的 "long int" 适用，使用它 */

#ifndef HAVE_INT64
typedef long int int64;
#endif
#ifndef HAVE_UINT64
typedef unsigned long int uint64;
#endif
#define INT64CONST(x)  (x##L)
#define UINT64CONST(x) (x##UL)
#elif defined(HAVE_LONG_LONG_INT_64)
/* 我们有对 "long long int" 的有效支持，使用这个 */

#ifndef HAVE_INT64
typedef long long int int64;
#endif
#ifndef HAVE_UINT64
typedef unsigned long long int uint64;
#endif
#define INT64CONST(x)  (x##LL)
#define UINT64CONST(x) (x##ULL)
#else
/* 既不是 HAVE_LONG_INT_64 也不是 HAVE_LONG_LONG_INT_64 */
#error must have a working 64-bit integer datatype
#endif

/* 用于 64 位整数的 snprintf 格式字符串 */
#define INT64_FORMAT "%" INT64_MODIFIER "d"
#define UINT64_FORMAT "%" INT64_MODIFIER "u"

/*
 * 128 位有符号和无符号整数
 *		目前对这类类型的支持仍然有限。
 *		例如，128 位字面量和 snprintf 不受支持；但数学运算是可以的。
 *		此外，因为在选择 MAXIMUM_ALIGNOF 时我们排除了这类类型，
 *		必须能够迫使编译器在不超过 MAXALIGN 边界上分配它们。
 */
#if defined(PG_INT128_TYPE)
#if defined(pg_attribute_aligned) || ALIGNOF_PG_INT128_TYPE <= MAXIMUM_ALIGNOF
#define HAVE_INT128 1

typedef PG_INT128_TYPE int128
#if defined(pg_attribute_aligned)
			pg_attribute_aligned(MAXIMUM_ALIGNOF)
#endif
		   ;

typedef unsigned PG_INT128_TYPE uint128
#if defined(pg_attribute_aligned)
			pg_attribute_aligned(MAXIMUM_ALIGNOF)
#endif
		   ;

#endif
#endif

/*
 * stdint.h 的限制不能保证与我们的固定宽度类型兼容。
 * 所以只需定义我们自己的类型。
 */
#define PG_INT8_MIN		(-0x7F-1)
#define PG_INT8_MAX		(0x7F)
#define PG_UINT8_MAX	(0xFF)
#define PG_INT16_MIN	(-0x7FFF-1)
#define PG_INT16_MAX	(0x7FFF)
#define PG_UINT16_MAX	(0xFFFF)
#define PG_INT32_MIN	(-0x7FFFFFFF-1)
#define PG_INT32_MAX	(0x7FFFFFFF)
#define PG_UINT32_MAX	(0xFFFFFFFFU)
#define PG_INT64_MIN	(-INT64CONST(0x7FFFFFFFFFFFFFFF) - 1)
#define PG_INT64_MAX	INT64CONST(0x7FFFFFFFFFFFFFFF)
#define PG_UINT64_MAX	UINT64CONST(0xFFFFFFFFFFFFFFFF)

/*
 * 我们现在总是使用 int64 时间戳，但为了外部代码的利益，
 * 保留此符号定义以供测试。
 */
#define HAVE_INT64_TIMESTAMP

/*
 * 大小
 *		任何内存驻留对象的大小，由 sizeof 返回。
 */
typedef size_t Size;

/*
 * 索引
 *		进入任何内存驻留数组的索引。
 *
 * 注意：
 *		索引为非负整数。
 */
typedef unsigned int Index;

/*
 * 偏移量
 *		进入任何内存驻留数组的偏移。
 *
 * 注意：
 *		这与索引不同，索引总是
 *		非负，而偏移量可能是负数。
 */
typedef signed int Offset;

/*
 * 常见的 Postgres 数据类型名称（如在目录中使用的）
 */
typedef float float4;
typedef double float8;

#ifdef USE_FLOAT8_BYVAL
#define FLOAT8PASSBYVAL true
#else
#define FLOAT8PASSBYVAL false
#endif

/*
 * Oid, RegProcedure, TransactionId, SubTransactionId, MultiXactId,
 * CommandId
 */

/* typedef Oid 在 postgres_ext.h 中 */

/*
 * regproc 是在包含/目录头文件中使用的类型名称，但
 * RegProcedure 是 C 代码中首选的名称。
 */
typedef Oid regproc;
typedef regproc RegProcedure;

typedef uint32 TransactionId;

typedef uint32 LocalTransactionId;

typedef uint32 SubTransactionId;

#define InvalidSubTransactionId		((SubTransactionId) 0)
#define TopSubTransactionId			((SubTransactionId) 1)

/* MultiXactId 必须等同于 TransactionId，以适应 t_xmax */
typedef TransactionId MultiXactId;

typedef uint32 MultiXactOffset;

typedef uint32 CommandId;

#define FirstCommandId	((CommandId) 0)
#define InvalidCommandId	(~(CommandId)0)


/* ----------------
 *		可变长度数据类型均共享 'struct varlena' 头部。
 *
 * 注意：对于 TOASTable 类型，这是一个过于简化的说法，因为值
 * 可能被压缩或移出行内。然而数据类型特定的例程
 * 大多数都是处理去 TOAST 的值，当然
 * 客户端的例程不应看到 TOAST 的值。但即使在去 TOAST 的值中，也应注意
 * 直接接触 vl_len_，因为它的
 * 表示不再方便。建议代码始终使用宏 VARDATA_ANY, VARSIZE_ANY, VARSIZE_ANY_EXHDR, VARDATA, VARSIZE,
 * 和 SET_VARSIZE，而不是依赖直接提及结构字段。
 * 请参见 postgres.h 以获取 TOAST 的形式的详细信息。
 * ----------------
 */
struct varlena
{
	char		vl_len_[4];		/* 不要直接触摸这个字段！ */
	char		vl_dat[FLEXIBLE_ARRAY_MEMBER];	/* 数据内容在这里 */
};

#define VARHDRSZ		((int32) sizeof(int32))

/*
 * 这些广泛使用的数据类型只是一个 varlena 头和数据字节。
 * 没有终止的空值或类似的东西——数据长度总是
 * VARSIZE_ANY_EXHDR(ptr)。
 */
typedef struct varlena bytea;
typedef struct varlena text;
typedef struct varlena BpChar;	/* 填充字符，即 SQL char(n) */
typedef struct varlena VarChar; /* 可变长度字符，即 SQL varchar(n) */

/*
 * 专用数组类型。这些在物理上与常规数组的布局相同
 * （以便常规数组下标代码可以与它们一起工作）。
 * 它们作为独立类型存在主要是出于历史原因：
 * 它们具有非标准的 I/O 行为，我们不想改变，
 * 以免破坏查看系统目录的应用程序。此外，
 * oidvector 还有一个实现问题：它是 pg_proc 的主键的一部分，
 * 我们不能在没有循环依赖的情况下使用正常的 btree 数组支持例程。
 */
typedef struct
{
	int32		vl_len_;		/* 这些字段必须与 ArrayType 匹配！ */
	int			ndim;			/* 对于 int2vector 始终为 1 */
	int32		dataoffset;		/* 对于 int2vector 始终为 0 */
	Oid			elemtype;
	int			dim1;
	int			lbound1;
	int16		values[FLEXIBLE_ARRAY_MEMBER];
} int2vector;

typedef struct
{
	int32		vl_len_;		/* 这些字段必须与 ArrayType 匹配！ */
	int			ndim;			/* 对于 oidvector 始终为 1 */
	int32		dataoffset;		/* 对于 oidvector 始终为 0 */
	Oid			elemtype;
	int			dim1;
	int			lbound1;
	Oid			values[FLEXIBLE_ARRAY_MEMBER];
} oidvector;

/*
 * 名称的表示：实质上只是一个 C 字符串，但用空字符填充到
 * 恰好 NAMEDATALEN 字节。使用结构体是出于历史原因。
 */
typedef struct nameData
{
	char		data[NAMEDATALEN];
} NameData;
typedef NameData *Name;

#define NameStr(name)	((name).data)


/* ----------------------------------------------------------------
 *				第 4 部分：系统类型的 IsValid 宏
 * ----------------------------------------------------------------
 */
/*
 * BoolIsValid
 *		如果 bool 是有效的，则为真。
 */
#define BoolIsValid(boolean)	((boolean) == false || (boolean) == true)

/*
 * PointerIsValid
 *		指针有效则返回真。
 */
#define PointerIsValid(pointer) ((const void*)(pointer) != NULL)

/*
 * PointerIsAligned
 *		指针正确对齐以指向给定类型时返回真。
 */
#define PointerIsAligned(pointer, type) \
		(((uintptr_t)(pointer) % (sizeof (type))) == 0)

#define OffsetToPointer(base, offset) \
		((void *)((char *) base + offset))

#define OidIsValid(objectId)  ((bool) ((objectId) != InvalidOid))

#define RegProcedureIsValid(p)	OidIsValid(p)


/* ----------------------------------------------------------------
 *				第5节：offsetof，lengthof，alignment
 * ----------------------------------------------------------------
 */
/*
 * offsetof
 *		结构/联合体字段在该结构/联合体中的偏移量。
 *
 *		XXX 这应该是stddef.h的一部分，但在某些系统上（如SunOS 4）并不存在。
 */
#ifndef offsetof
#define offsetof(type, field)	((long) &((type *)0)->field)
#endif							/* offsetof */

/*
 * lengthof
 *		数组中的元素数量。
 */
#define lengthof(array) (sizeof (array) / sizeof ((array)[0]))

/* ----------------
 * 对齐宏：适当地将长度或地址对齐到给定类型。
 * fooALIGN()宏将向上舍入到所需对齐的倍数，
 * 而fooALIGN_DOWN()宏将向下舍入。后者在诸如“有多少个X大小的结构可以放入一页？”等问题中更有用。
 *
 * 注意：TYPEALIGN[_DOWN]在ALIGNVAL不是2的幂时将无效。
 * 然而，这种情况在实际中似乎极不可能需要。
 *
 * 注意：MAXIMUM_ALIGNOF，因此MAXALIGN()，故意排除了编译器可能具有的任何大于8字节的类型。
 * ----------------
 */

#define TYPEALIGN(ALIGNVAL,LEN)  \
	(((uintptr_t) (LEN) + ((ALIGNVAL) - 1)) & ~((uintptr_t) ((ALIGNVAL) - 1)))

#define SHORTALIGN(LEN)			TYPEALIGN(ALIGNOF_SHORT, (LEN))
#define INTALIGN(LEN)			TYPEALIGN(ALIGNOF_INT, (LEN))
#define LONGALIGN(LEN)			TYPEALIGN(ALIGNOF_LONG, (LEN))
#define DOUBLEALIGN(LEN)		TYPEALIGN(ALIGNOF_DOUBLE, (LEN))
#define MAXALIGN(LEN)			TYPEALIGN(MAXIMUM_ALIGNOF, (LEN))
/* MAXALIGN仅涵盖内置类型，而不是缓冲区 */
#define BUFFERALIGN(LEN)		TYPEALIGN(ALIGNOF_BUFFER, (LEN))
#define CACHELINEALIGN(LEN)		TYPEALIGN(PG_CACHE_LINE_SIZE, (LEN))

#define TYPEALIGN_DOWN(ALIGNVAL,LEN)  \
	(((uintptr_t) (LEN)) & ~((uintptr_t) ((ALIGNVAL) - 1)))

#define SHORTALIGN_DOWN(LEN)	TYPEALIGN_DOWN(ALIGNOF_SHORT, (LEN))
#define INTALIGN_DOWN(LEN)		TYPEALIGN_DOWN(ALIGNOF_INT, (LEN))
#define LONGALIGN_DOWN(LEN)		TYPEALIGN_DOWN(ALIGNOF_LONG, (LEN))
#define DOUBLEALIGN_DOWN(LEN)	TYPEALIGN_DOWN(ALIGNOF_DOUBLE, (LEN))
#define MAXALIGN_DOWN(LEN)		TYPEALIGN_DOWN(MAXIMUM_ALIGNOF, (LEN))
#define BUFFERALIGN_DOWN(LEN)	TYPEALIGN_DOWN(ALIGNOF_BUFFER, (LEN))

/*
 * 上述宏在uintptr_t更宽的类型上将无法工作，如在32位平台上的uint64。
 * 对于指针或长度通常对齐的常规用法，这不是问题，
 * 但对于需要对齐（可能）更宽的某些情况，请使用TYPEALIGN64。
 */
#define TYPEALIGN64(ALIGNVAL,LEN)  \
	(((uint64) (LEN) + ((ALIGNVAL) - 1)) & ~((uint64) ((ALIGNVAL) - 1)))

/* 我们当前不需要其他ALIGN宏的更宽版本 */
#define MAXALIGN64(LEN)			TYPEALIGN64(MAXIMUM_ALIGNOF, (LEN))


/* ----------------------------------------------------------------
 *				第6节：断言
 * ----------------------------------------------------------------
 */

/*
 * 如果定义USE_ASSERT_CHECKING，将打开所有断言。
 * - plai  1990年9月5日
 *
 * 不应在发布或基准副本中定义它
 */

/*
 * Assert()可在前端和后端代码中使用。在前端代码中，它仅调用标准的assert（如果可用）。
 * 如果未配置使用断言，则不执行任何操作。
 */
#ifndef USE_ASSERT_CHECKING

#define Assert(condition)	((void)true)
#define AssertMacro(condition)	((void)true)
#define AssertArg(condition)	((void)true)
#define AssertState(condition)	((void)true)
#define AssertPointerAlignment(ptr, bndr)	((void)true)
#define Trap(condition, errorType)	((void)true)
#define TrapMacro(condition, errorType) (true)

#elif defined(FRONTEND)

#include <assert.h>
#define Assert(p) assert(p)
#define AssertMacro(p)	((void) assert(p))
#define AssertArg(condition) assert(condition)
#define AssertState(condition) assert(condition)
#define AssertPointerAlignment(ptr, bndr)	((void)true)

#else							/* USE_ASSERT_CHECKING && !FRONTEND */

/*
 * Trap
 *		如果给定条件为真，则生成异常。
 */
#define Trap(condition, errorType) \
	do { \
		if (condition) \
			ExceptionalCondition(#condition, (errorType), \
								 __FILE__, __LINE__); \
	} while (0)

/*
 *	TrapMacro与Trap相同，但它旨在在宏中使用：
 *
 *		#define foo(x) (AssertMacro(x != 0), bar(x))
 *
 *	CPP多有趣？
 */
#define TrapMacro(condition, errorType) \
	((bool) (! (condition) || \
			 (ExceptionalCondition(#condition, (errorType), \
								   __FILE__, __LINE__), 0)))

#define Assert(condition) \
	do { \
		if (!(condition)) \
			ExceptionalCondition(#condition, "FailedAssertion", \
								 __FILE__, __LINE__); \
	} while (0)

#define AssertMacro(condition) \
	((void) ((condition) || \
			 (ExceptionalCondition(#condition, "FailedAssertion", \
								   __FILE__, __LINE__), 0)))

#define AssertArg(condition) \
	do { \
		if (!(condition)) \
			ExceptionalCondition(#condition, "BadArgument", \
								 __FILE__, __LINE__); \
	} while (0)

#define AssertState(condition) \
	do { \
		if (!(condition)) \
			ExceptionalCondition(#condition, "BadState", \
								 __FILE__, __LINE__); \
	} while (0)

/*
 * 检查`ptr`是否与`bndr`对齐。
 */
#define AssertPointerAlignment(ptr, bndr) \
	Trap(TYPEALIGN(bndr, (uintptr_t)(ptr)) != (uintptr_t)(ptr), \
		 "UnalignedPointer")

#endif							/* USE_ASSERT_CHECKING && !FRONTEND */

/*
 * ExceptionalCondition是否在后端编译，无论USE_ASSERT_CHECKING是否定义，以支持使用以该#define构建的扩展
 * 与未定义的后端。因此，只要！FRONTEND，我们就应声明它。
 */
#ifndef FRONTEND
extern void ExceptionalCondition(const char *conditionName,
								 const char *errorType,
								 const char *fileName, int lineNumber) pg_attribute_noreturn();
#endif

/*
 * 用于支持编译时断言检查的宏。
 *
 * 如果“条件”（编译时常量表达式）评估为假，
 * 则使用“errmessage”（字符串字面量）抛出编译错误。
 *
 * gcc 4.6及更高版本支持_Static_assert()，但存在奇怪的语法位置限制。
 * 宏StaticAssertStmt()和StaticAssertExpr()使其在语句或表达式中安全使用。
 * 宏StaticAssertDecl()适合在文件作用域使用（在任何函数外）。
 *
 * 否则，我们退回到一个假设编译器会抱怨结构位域的负宽度的克隆。
 * 这不会包含有用的错误信息，但总比根本没有错误要好。
 */
#ifndef __cplusplus
#ifdef HAVE__STATIC_ASSERT
#define StaticAssertStmt(condition, errmessage) \
	do { _Static_assert(condition, errmessage); } while(0)
#define StaticAssertExpr(condition, errmessage) \
	((void) ({ StaticAssertStmt(condition, errmessage); true; }))
#define StaticAssertDecl(condition, errmessage) \
	_Static_assert(condition, errmessage)
#else							/* !HAVE__STATIC_ASSERT */
#define StaticAssertStmt(condition, errmessage) \
	((void) sizeof(struct { int static_assert_failure : (condition) ? 1 : -1; }))
#define StaticAssertExpr(condition, errmessage) \
	StaticAssertStmt(condition, errmessage)
#define StaticAssertDecl(condition, errmessage) \
	extern void static_assert_func(int static_assert_failure[(condition) ? 1 : -1])
#endif							/* HAVE__STATIC_ASSERT */
#else							/* C++ */
#if defined(__cpp_static_assert) && __cpp_static_assert >= 200410
#define StaticAssertStmt(condition, errmessage) \
	static_assert(condition, errmessage)
#define StaticAssertExpr(condition, errmessage) \
	({ static_assert(condition, errmessage); })
#define StaticAssertDecl(condition, errmessage) \
	static_assert(condition, errmessage)
#else							/* !__cpp_static_assert */
#define StaticAssertStmt(condition, errmessage) \
	do { struct static_assert_struct { int static_assert_failure : (condition) ? 1 : -1; }; } while(0)
#define StaticAssertExpr(condition, errmessage) \
	((void) ({ StaticAssertStmt(condition, errmessage); }))
#define StaticAssertDecl(condition, errmessage) \
	extern void static_assert_func(int static_assert_failure[(condition) ? 1 : -1])
#endif							/* __cpp_static_assert */
#endif							/* C++ */


/*
 * 对变量（或表达式）进行编译时检查，确保其具有指定类型。
 *
 * AssertVariableIsOfType()可以作为语句使用。
 * AssertVariableIsOfTypeMacro()旨在用于宏，例如
 *		#define foo(x) (AssertVariableIsOfTypeMacro(x, int), bar(x))
 *
 * 如果我们没有__builtin_types_compatible_p，我们仍然可以断言类型具有相同的大小。
 * 这远非理想（特别是在32位平台上），但至少提供了一些覆盖。
 */
#ifdef HAVE__BUILTIN_TYPES_COMPATIBLE_P
#define AssertVariableIsOfType(varname, typename) \
	StaticAssertStmt(__builtin_types_compatible_p(__typeof__(varname), typename), \
	CppAsString(varname) " does not have type " CppAsString(typename))
#define AssertVariableIsOfTypeMacro(varname, typename) \
	(StaticAssertExpr(__builtin_types_compatible_p(__typeof__(varname), typename), \
	 CppAsString(varname) " does not have type " CppAsString(typename)))
#else							/* !HAVE__BUILTIN_TYPES_COMPATIBLE_P */
#define AssertVariableIsOfType(varname, typename) \
	StaticAssertStmt(sizeof(varname) == sizeof(typename), \
	CppAsString(varname) " does not have type " CppAsString(typename))
#define AssertVariableIsOfTypeMacro(varname, typename) \
	(StaticAssertExpr(sizeof(varname) == sizeof(typename), \
	 CppAsString(varname) " does not have type " CppAsString(typename)))
#endif							/* HAVE__BUILTIN_TYPES_COMPATIBLE_P */


/* ----------------------------------------------------------------
 *				第7节：广泛使用的宏
 * ----------------------------------------------------------------
 */
/*
 * Max
 *		返回两个数字中的最大值。
 */
#define Max(x, y)		((x) > (y) ? (x) : (y))

/*
 * Min
 *		返回两个数字中的最小值。
 */
#define Min(x, y)		((x) < (y) ? (x) : (y))

/*
 * Abs
 *		返回参数的绝对值。
 */
#define Abs(x)			((x) >= 0 ? (x) : -(x))


/* 获取在非长对齐地址中设置的位的位掩码 */
#define LONG_ALIGN_MASK (sizeof(long) - 1)


/*
 * MemSet
 *	与标准库函数 memset() 完全相同，但在将小的字对齐结构（例如 parsetree 节点）清零时速度明显更快。
 *	这是一个宏，因为主要目的是避免函数调用的开销。然而，我们还发现，在某些平台上，
 *	即使那些具有汇编 memset() 函数的平台，循环的速度也比原生 libc memset() 更快。
 *	还需要进行更多的研究，也许可以在配置中进行 MEMSET_LOOP_LIMIT 测试。
 */
#define MemSet(start, val, len) \
	do \
	{ \
		/* 必须为 void*，因为我们还不知道它是否已对齐为整数 */ \
		void   *_vstart = (void *) (start); \
		int		_val = (val); \
		Size	_len = (len); \
\
		if ((((uintptr_t) _vstart) & LONG_ALIGN_MASK) == 0 && \
			(_len & LONG_ALIGN_MASK) == 0 && \
			_val == 0 && \
			_len <= MEMSET_LOOP_LIMIT && \
			/* \
			 *	如果 MEMSET_LOOP_LIMIT == 0，优化器应该在编译时发现 \
			 *	整个“if”是假的。 \
			 */ \
			MEMSET_LOOP_LIMIT != 0) \
		{ \
			long *_start = (long *) _vstart; \
			long *_stop = (long *) ((char *) _start + _len); \
			while (_start < _stop) \
				*_start++ = 0; \
		} \
		else \
			memset(_vstart, _val, _len); \
	} while (0)

/*
 * MemSetAligned 与 MemSet 相同，除了它省略了测试 "start" 是否为字对齐的检查。
 * 如果调用者事先知道指针是适当对齐的（通常因为他刚从 palloc() 中获得它，
 * palloc() 始终返回最大对齐的指针），那么使用它是可以的。
 */
#define MemSetAligned(start, val, len) \
	do \
	{ \
		long   *_start = (long *) (start); \
		int		_val = (val); \
		Size	_len = (len); \
\
		if ((_len & LONG_ALIGN_MASK) == 0 && \
			_val == 0 && \
			_len <= MEMSET_LOOP_LIMIT && \
			MEMSET_LOOP_LIMIT != 0) \
		{ \
			long *_stop = (long *) ((char *) _start + _len); \
			while (_start < _stop) \
				*_start++ = 0; \
		} \
		else \
			memset(_start, _val, _len); \
	} while (0)


/*
 * MemSetTest/MemSetLoop 是一个变体版本，允许在 "val" 和 "len" 是常量 *且* 
 * 我们知道 "start" 指针必须是字对齐的情况下，在编译时进行 MemSet 中的所有测试。
 * 如果 MemSetTest 成功，那么使用 MemSetLoop 是可以的，否则使用 MemSetAligned。
 * 使用这种方法时要注意参数的多次评估。
 */
#define MemSetTest(val, len) \
	( ((len) & LONG_ALIGN_MASK) == 0 && \
	(len) <= MEMSET_LOOP_LIMIT && \
	MEMSET_LOOP_LIMIT != 0 && \
	(val) == 0 )

#define MemSetLoop(start, val, len) \
	do \
	{ \
		long * _start = (long *) (start); \
		long * _stop = (long *) ((char *) _start + (Size) (len)); \
	\
		while (_start < _stop) \
			*_start++ = 0; \
	} while (0)

/*
 * 用于在转换为整数之前检查浮点值范围的宏。
 * 我们必须小心边界值在浮点域中被精确表示。 PG_INTnn_MIN 是一个精确的 2 的幂，因此它会被精确表示；但 PG_INTnn_MAX 不是，可能会被舍入，所以要避免使用它。
 * 输入必须提前四舍五入为整数，通常使用 rint()，否则我们可能会对接近极限的值得出错误的结论。
 * 这些宏会对 Inf 做正确的处理，但不一定对 NaN，所以如果有这种可能性，请先检查 isnan(num)。
 */
#define FLOAT4_FITS_IN_INT16(num) \
	((num) >= (float4) PG_INT16_MIN && (num) < -((float4) PG_INT16_MIN))
#define FLOAT4_FITS_IN_INT32(num) \
	((num) >= (float4) PG_INT32_MIN && (num) < -((float4) PG_INT32_MIN))
#define FLOAT4_FITS_IN_INT64(num) \
	((num) >= (float4) PG_INT64_MIN && (num) < -((float4) PG_INT64_MIN))
#define FLOAT8_FITS_IN_INT16(num) \
	((num) >= (float8) PG_INT16_MIN && (num) < -((float8) PG_INT16_MIN))
#define FLOAT8_FITS_IN_INT32(num) \
	((num) >= (float8) PG_INT32_MIN && (num) < -((float8) PG_INT32_MIN))
#define FLOAT8_FITS_IN_INT64(num) \
	((num) >= (float8) PG_INT64_MIN && (num) < -((float8) PG_INT64_MIN))


/* ----------------------------------------------------------------
 *				第 8 部分：随机内容
 * ----------------------------------------------------------------
 */

#ifdef HAVE_STRUCT_SOCKADDR_UN
#define HAVE_UNIX_SOCKETS 1
#endif

/*
 * 反转 qsort 风格比较结果的符号，即交换负数和正整数值，注意不要为 INT_MIN 得到错误的答案。 参数应该是一个整数变量。
 */
#define INVERT_COMPARE_RESULT(var) \
	((var) = ((var) < 0) ? 1 : -(var))

/*
 * 使用这个，而不是 "char buf[BLCKSZ]"，来声明一个字段或局部变量，用于保存页面缓冲区，如果该页面可能被作为页面访问，而不只是字节字符串。 否则，该变量可能会未对齐，从而在需要对齐的硬件上导致问题。 （在某些地方，我们使用这个来声明缓冲区，即使我们只将它们传递给 read() 和 write()，因为从对齐的缓冲区复制到 / 从未对齐的缓冲区通常更快。）我们在联合体中包含了 "double" 和 "int64"，以确保编译器知道值必须进行 MAXALIGN（参见 configure 计算 MAXIMUM_ALIGNOF）。
 */
typedef union PGAlignedBlock
{
	char		data[BLCKSZ];
	double		force_align_d;
	int64		force_align_i64;
} PGAlignedBlock;

/* 同上，但用于一个 XLOG_BLCKSZ 大小的缓冲区 */
typedef union PGAlignedXLogBlock
{
	char		data[XLOG_BLCKSZ];
	double		force_align_d;
	int64		force_align_i64;
} PGAlignedXLogBlock;

/* 字符的 msb */
#define HIGHBIT					(0x80)
#define IS_HIGHBIT_SET(ch)		((unsigned char)(ch) & HIGHBIT)

/*
 * 支持用于转义字符串的宏。 如果生成非标准符合字符串，escape_backslash 应该为真。 在字符串前加上 ESCAPE_STRING_SYNTAX 可以确保它是非标准符合的。
 * 注意 "ch" 参数的多次求值！
 */
#define SQL_STR_DOUBLE(ch, escape_backslash)	\
	((ch) == '\'' || ((ch) == '\\' && (escape_backslash)))

#define ESCAPE_STRING_SYNTAX	'E'


#define STATUS_OK				(0)
#define STATUS_ERROR			(-1)
#define STATUS_EOF				(-2)

/*
 * gettext 支持
 */

#ifndef ENABLE_NLS
/* 我们本来可以从 <libintl.h> 获得的内容 */
#define gettext(x) (x)
#define dgettext(d,x) (x)
#define ngettext(s,p,n) ((n) == 1 ? (s) : (p))
#define dngettext(d,s,p,n) ((n) == 1 ? (s) : (p))
#endif

#define _(x) gettext(x)

/*
 * 使用这个来标记字符串常量在某个稍后的时间需要翻译，而不是立即。 这对于需要访问原始字符串和翻译字符串的情况有用，对于立即翻译不可能的情况，如初始化全局变量时也是如此。
 *
 * https://www.gnu.org/software/gettext/manual/html_node/Special-cases.html
 */
#define gettext_noop(x) (x)

/*
 * 为了更好地支持主要 PostgreSQL 版本的并行安装以及主要库 soname 版本的并行安装，我们通过附加这些版本号来修改 gettext 域名。 编码规则应该是，凡是字面提到域名的地方，都必须用 PG_TEXTDOMAIN() 包裹。 以下宏不适用于非字面量；但这在某种程度上是有意的，因为它避免了在值传递时需要担心多个状态的预处理和后处理。
 *
 * 确保这与 nls-global.mk 中的安装规则匹配。
 */
#ifdef SO_MAJOR_VERSION
#define PG_TEXTDOMAIN(domain) (domain CppAsString2(SO_MAJOR_VERSION) "-" PG_MAJORVERSION)
#else
#define PG_TEXTDOMAIN(domain) (domain "-" PG_MAJORVERSION)
#endif

/*
 * 允许从表达式中去掉常量性和易变性，但不允许更改底层类型的宏。 目前，后者的强制执行只适用于 gcc 等编译器。
 *
 * 请注意，如果结果将被修改，去掉常量性是不安全的（这将导致未定义行为）。 这样做可能导致编译器错误优化或运行时崩溃（修改只读内存）。 只有在结果不会被修改的情况下使用是安全的，但 API 设计或语言限制阻止您声明这一点（例如，因为一个函数返回常量和非常量变量）。
 *
 * 注意，这仅在函数作用域内有效，而不适用于全局变量（这很好，但改进这一点并不简单）。
 */
#if defined(HAVE__BUILTIN_TYPES_COMPATIBLE_P)
#define unconstify(underlying_type, expr) \
	(StaticAssertExpr(__builtin_types_compatible_p(__typeof(expr), const underlying_type), \
					  "wrong cast"), \
	 (underlying_type) (expr))
#define unvolatize(underlying_type, expr) \
	(StaticAssertExpr(__builtin_types_compatible_p(__typeof(expr), volatile underlying_type), \
					  "wrong cast"), \
	 (underlying_type) (expr))
#else
#define unconstify(underlying_type, expr) \
	((underlying_type) (expr))
#define unvolatize(underlying_type, expr) \
	((underlying_type) (expr))
#endif

/* ----------------------------------------------------------------
 *				第 9 部分：特定于系统的 hacks
 *
 *		这应该仅限于绝对必须包含在每个源文件中的内容。 特定于端口的头文件通常是处理此类事情的更好地方。
 * ----------------------------------------------------------------
 */

/*
 * 注意：这也用于打开文本文件。 WIN32 在以文本模式打开的文件中将 Control-Z 视为 EOF。
 * 因此，我们在 Win32 上以二进制模式打开文件，以便可以读取字面控制 Z。 另一个影响是我们看到 CRLF，但这没关系，因为我们已经可以干净地处理这些。
 */
#if defined(WIN32) || defined(__CYGWIN__)
#define PG_BINARY	O_BINARY
#define PG_BINARY_A "ab"
#define PG_BINARY_R "rb"
#define PG_BINARY_W "wb"
#else
#define PG_BINARY	0
#define PG_BINARY_A "a"
#define PG_BINARY_R "r"
#define PG_BINARY_W "w"
#endif

/*
 * 为特定机器的标准 C 库中不存在的例程提供原型。
 */

#if defined(HAVE_FDATASYNC) && !HAVE_DECL_FDATASYNC
extern int	fdatasync(int fildes);
#endif

/* 较旧的平台可能以其他名称提供 strto[u]ll 功能 */
#if !defined(HAVE_STRTOLL) && defined(HAVE___STRTOLL)
#define strtoll __strtoll
#define HAVE_STRTOLL 1
#endif

#if !defined(HAVE_STRTOLL) && defined(HAVE_STRTOQ)
#define strtoll strtoq
#define HAVE_STRTOLL 1
#endif

#if !defined(HAVE_STRTOULL) && defined(HAVE___STRTOULL)
#define strtoull __strtoull
#define HAVE_STRTOULL 1
#endif

#if !defined(HAVE_STRTOULL) && defined(HAVE_STRTOUQ)
#define strtoull strtouq
#define HAVE_STRTOULL 1
#endif

#if defined(HAVE_STRTOLL) && !HAVE_DECL_STRTOLL
extern long long strtoll(const char *str, char **endptr, int base);
#endif

#if defined(HAVE_STRTOULL) && !HAVE_DECL_STRTOULL
extern unsigned long long strtoull(const char *str, char **endptr, int base);
#endif


/*
 * 薄包装，将字符串转换为精确的64位整数，匹配我们的
 * int64 定义。 （在命名上，比较一下 POSIX 的
 * strtoimax()/strtoumax()，返回 intmax_t/uintmax_t。）
 */
#ifdef HAVE_LONG_INT_64
#define strtoi64(str, endptr, base) ((int64) strtol(str, endptr, base))
#define strtou64(str, endptr, base) ((uint64) strtoul(str, endptr, base))
#else
#define strtoi64(str, endptr, base) ((int64) strtoll(str, endptr, base))
#define strtou64(str, endptr, base) ((uint64) strtoull(str, endptr, base))
#endif

/*
 * 使用 "extern PGDLLIMPORT ..." 来声明在核心后端定义
 * 的变量，并且需要被可加载模块访问。
 * 在大多数平台上不需要特殊标记。
 */
#ifndef PGDLLIMPORT
#define PGDLLIMPORT
#endif

/*
 * 使用 "extern PGDLLEXPORT ..." 来声明在
 * 可加载模块中定义的函数，并且需要能够被核心后端调用。
 * （通常，这不是必要的，因为我们的构建过程会自动导出
 * 这样的符号，但有时需要手动标记。）
 * 在大多数平台上不需要特殊标记。
 */
#ifndef PGDLLEXPORT
#define PGDLLEXPORT
#endif

/*
 * 以下内容用作信号处理程序的参数列表。
 * 任何使用除 int 参数之外的其他参数的端口都应该在
 * 其 pg_config_os.h 文件中重写此内容。 
 * 请注意，变量名称是必需的，因为它在原型和定义中都使用。
 * 另请注意这个较长的名称。我们预计这不会与其他名称冲突，
 * 导致编译器警告。
 */

#ifndef SIGNAL_ARGS
#define SIGNAL_ARGS  int postgres_signal_arg
#endif

/*
 * 当没有 sigsetjmp 时，它的功能由普通的 setjmp 提供。
 * 我们现在仅在 Windows 上支持此情况。
 * 然而，似乎 MinGW-64 在其 setjmp 支持上存在一些长期问题，
 * 因此在该工具链上我们作弊并使用 gcc 的内建函数。
 */
#ifdef WIN32
#ifdef __MINGW64__
typedef intptr_t sigjmp_buf[5];
#define sigsetjmp(x,y) __builtin_setjmp(x)
#define siglongjmp __builtin_longjmp
#else							/* !__MINGW64__ */
#define sigjmp_buf jmp_buf
#define sigsetjmp(x,y) setjmp(x)
#define siglongjmp longjmp
#endif							/* __MINGW64__ */
#endif							/* WIN32 */

/* EXEC_BACKEND 定义 */
#ifdef EXEC_BACKEND
#define NON_EXEC_STATIC
#else
#define NON_EXEC_STATIC static
#endif

/* /端口兼容函数 */
#include "port.h"

#endif							/* C_H */
